package com.siyu.model.dispatch.algorithm;

import com.siyu.model.dispatch.algorithm.interfaces.SingleReservoirInterface;
import com.siyu.model.dispatch.io.SingleDamInput;
import com.siyu.model.dispatch.io.SingleDamOutput;
import com.siyu.model.dispatch.io.SingleSluiceOutput;
import com.siyu.model.dispatch.utils.CalculateUtils;
import com.siyu.model.dispatch.utils.CurveDataItem;
import com.siyu.model.dispatch.utils.ReserviorTimeResult;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

public class ConventionalSingleReservoir implements SingleReservoirInterface {
    // 说明：防洪调度优化方法，最大削峰准则，保护下游安全

    String ReservoirName = "";  // 水库名称

    String ReservoirID = "";  // 水库ID

    String startTime = "";  // 起始时间

    String strResCode = "";  // 水库编号 用于提取对应水库的数据时使用

    double DeadLevel = 0;  // 死水位

    double NormalLevel = 0;  // 正常蓄水位

    double FloodLevel = 0;  // 汛限水位

    double QDWaterLevel = 0;  // 起调水位

    double SJWaterLevel = 0;  // 设计洪水位

    double FCQrelease = 0;  // 非常泄洪

    double XZQrelease = 0;  // 限制下泄

    double ANQrelease = 0;  // 下游河道安全下泄

    double CHQrelease = 0;  // 敞泄流量

    int NumOperationYear = 1;  // 长系列年数

    int NumPeriod = 0;  // 一年内的时段数

    int TotalNumPeriod = 0;  // 长系列所有时段总数

    private int PeriodLength = 0;  // 时段长度 单位（s）

    double[][] OperationRunoffData = null;  // 长系列径流过程

    double[][] IntervalRunoffData = null;  // 下游区间径流过程

    double[] UpLevelLimitArray = null;  // 各时段水位上限

    double[] DownLevelLimitArray = null;  // 各时段水位下限

    double[] UpVolumeLimitArray = null;  // 各时段库容上限

    double[] DownVolumeLimitArray = null;  // 各时段库容下限

    double[] UpQreleaseLimitArray = null;  // 各时段下泄流量上限

    double[] DownQreleaseLimitArray = null;  // 各时段下泄流量下限

    double[] EvaLoss = null;  // 全年各时段蒸发量 单位：m^3/s

    int CurveExPLength = 0;  // 预想出力曲线点数

    double[][] CurveExP = null;  // 预想出力曲线 水头(m)、限制出力值(KW)

    int CurveLVLength = 0;  // 水位库容关系曲线点数

    double[][] CurveLV = null;  // 水位库容关系曲线 水位(m)、库容(亿m^3)

    int CurveHRLength = 0;  // 水头耗水率关系曲线点数

    double[][] CurveHR = null;  // 水头耗水率关系曲线 水头(m)、耗水率(m^3/kWh)

    int CurveLQLength = 0;  // 泄流能力关系曲线点数

    double[][] CurveLQ = null;  // 泄流能力关系曲线 水位(m)、流量(m^3/s)

    int CurveDLQLength = 0;  // 下游水位流量关系曲线点数

    double[][] CurveDLQ = null;  // 下游水位流量关系曲线 水位(m)、流量(m^3/s)

    double FloodPeak = 0;  // 入库洪峰 - max{长系列径流过程}

    double FloodVolume = 0;  // 入库洪量

    double FloodDischarge = 0;  // 最大泄洪

    double MaxLevel = 0;  // 最高水位

    double FloodRetention = 0;  // 拦蓄洪量

    double PeakClippingRate = 0;  // 削峰率

    private String folderPath;  // 输入数据文件路径

    private double MeanQrelease;  // Qmin 同始末水位算得

    private double MeanLevel;

    private double InitialLevel;  // 初始水位

    private String RKPeakTime;  // 入库峰现时间

    private String MaxLevelTime;  // 最高水位出现时间

    private String CKPeakTime; // 出库峰现时间

    private boolean MaxLevelExceedStandard;  // 最高水位是否超标准

    private boolean MaxLevelExceedLimit;  // 最高水位是否超汛限

    private boolean EndLevelExceedLimit;  // 末水位是否超过汛期

    private double CKVolume;  // 出库洪量

    private double EndLevel; // 期末水位

    private double[] tempQ;  // TODO: 待补充定义

    private MuskingenMethod oneChannel;

    private SingleDamOutput singleDamOutput;  // 单水库结果

    private SingleSluiceOutput singleSluiceOutput;  // 单闸坝结果

    double[][] WaterLevelArray = null;  //水位离散值

    double[][] WaterVolumeArray = null;  //库容离散值

    double[][] QreleaseArray = null;  //各时段下泄流量

    /*
     * 构造器和 getter、setter
     */
    public ConventionalSingleReservoir() {

    }

    public ConventionalSingleReservoir(SingleDamInput SingleDamInput, String folderPath, String strResCode, double meanQrelease, double meanLevel, MuskingenMethod oneChannel) {
        this.folderPath = folderPath;
        this.strResCode = strResCode;
        this.MeanQrelease = meanQrelease;
        this.MeanLevel = meanLevel;

        ReservoirName = SingleDamInput.getReservoirName();
        ReservoirID = SingleDamInput.getReservoirID();
        DeadLevel = SingleDamInput.getDeadLevel();
        NormalLevel = SingleDamInput.getNormalLevel();
        FloodLevel = SingleDamInput.getFloodLevel();
        FloodPeak = SingleDamInput.getFloodPeak();
        FloodVolume = SingleDamInput.getFloodVolume();
        NumPeriod = SingleDamInput.getNumPeriod();
        PeriodLength = SingleDamInput.getPeriodLength();
        startTime = SingleDamInput.getStartTime();

        QDWaterLevel = SingleDamInput.getQDWaterLevel();
        SJWaterLevel = SingleDamInput.getDesignWaterLevel();
        FCQrelease = SingleDamInput.getFCQrelease();
        XZQrelease = SingleDamInput.getXZQrelease();
        ANQrelease = SingleDamInput.getANQrelease();
        CHQrelease = SingleDamInput.getCHQrelease();

        TotalNumPeriod = NumOperationYear * NumPeriod;
        OperationRunoffData = new double[NumOperationYear][NumPeriod];
        IntervalRunoffData = new double[NumOperationYear][NumPeriod];

        List<Double> TempOperationRunoffData = SingleDamInput.getOperationRunoffData();
        for (int j = 0; j < TempOperationRunoffData.size(); j++) {
            OperationRunoffData[0][j] = TempOperationRunoffData.get(j);
        }

        List<Double> TempIntervalRunoffData = SingleDamInput.getIntervalRunoffData();
        for (int j = 0; j < TempIntervalRunoffData.size(); j++) {
            IntervalRunoffData[0][j] = TempIntervalRunoffData.get(j);
        }

        UpLevelLimitArray = new double[NumPeriod + 1];
        DownLevelLimitArray = new double[NumPeriod + 1];

        List<Double> TempUpLevelLimitArray = SingleDamInput.getUpLevelLimitArray();
        for (int j = 0; j < TempUpLevelLimitArray.size(); j++) {
            UpLevelLimitArray[j] = TempUpLevelLimitArray.get(j);
        }

        List<Double> TempDownLevelLimitArray = SingleDamInput.getDownLevelLimitArray();
        for (int j = 0; j < TempDownLevelLimitArray.size(); j++) {
            DownLevelLimitArray[j] = TempDownLevelLimitArray.get(j);
        }

        UpQreleaseLimitArray = new double[NumPeriod];
        DownQreleaseLimitArray = new double[NumPeriod];

        List<Double> TempUpQreleaseLimitArray = SingleDamInput.getUpQreleaseLimitArray();
        for (int j = 0; j < TempUpQreleaseLimitArray.size(); j++) {
            UpQreleaseLimitArray[j] = TempUpQreleaseLimitArray.get(j);
        }

        List<Double> TempDownQreleaseLimitArray = SingleDamInput.getDownQreleaseLimitArray();
        for (int j = 0; j < TempDownQreleaseLimitArray.size(); j++) {
            DownQreleaseLimitArray[j] = TempDownQreleaseLimitArray.get(j);
        }

        EvaLoss = new double[NumPeriod];

        List<Double> TempEvaLoss = SingleDamInput.getEvaLoss();
        for (int j = 0; j < TempEvaLoss.size(); j++) {
            EvaLoss[j] = TempEvaLoss.get(j);
        }

        List<CurveDataItem> TempCurveLV = SingleDamInput.getCurveLV();
        CurveLVLength = TempCurveLV.size();
        CurveLV = new double[CurveLVLength][2];
        for (int j = 0; j < TempCurveLV.size(); j++) {
            CurveLV[j][0] = TempCurveLV.get(j).getX1();
            CurveLV[j][1] = TempCurveLV.get(j).getY1();
        }

        List<CurveDataItem> TempCurveLQ = SingleDamInput.getCurveLQ();
        CurveLQLength = TempCurveLQ.size();
        CurveLQ = new double[CurveLQLength][2];
        for (int j = 0; j < TempCurveLQ.size(); j++) {
            CurveLQ[j][0] = TempCurveLQ.get(j).getX1();
            CurveLQ[j][1] = TempCurveLQ.get(j).getY1();
        }
        this.oneChannel = oneChannel;
    }

    public SingleDamOutput getSingleDamOutput() {
        return singleDamOutput;
    }

    public double[][] getOptimalLine_Qrelease() {
        return QreleaseArray;
    }

    public double[][] getIntervalRunoffData() {
        return IntervalRunoffData;
    }

    public int getPeriodLength() {
        return PeriodLength;
    }

    public double[] getTempQ() {
        return tempQ;
    }

    /*
     * 主要计算过程、属性赋值操作
     */
    public void calculate() throws Exception {

        NumOperationYear = 1;  // 因为算短期，不会超过一年，所以这儿统一定为1

        WaterLevelArray = new double[1][NumPeriod + 1];
        WaterVolumeArray = new double[1][NumPeriod + 1];
        QreleaseArray = new double[1][NumPeriod];

        WaterLevelArray[0][0] = QDWaterLevel;//起调水位
        WaterVolumeArray[0][0] = CalculateUtils.getVolumeFromLV(WaterLevelArray[0][0], CurveLV, CurveLVLength);//时初库容

        // 按常规调度规则控制调度
        double[][] OutputArray = new double[3][NumPeriod + 1];

        double BeginWaterLevel = 0;//时初水位：汛限水位
        double CalculatedEndWaterLevel = 0; //计算得到的末水位
        double AssumedEndWaterLevel = 0; //假设的末水位
        double BeginWaterVolume = WaterVolumeArray[0][0];//时初库容
        double EndWaterVolume = 0;
        BeginWaterLevel = WaterLevelArray[0][0];


        for (int j = 0; j <= NumPeriod - 1; j++) {
            double MaxQelease = 0;
            double Qrelease = 0;

            // 先假设一个末水位，获取末库容
            AssumedEndWaterLevel = BeginWaterLevel;
            CalculatedEndWaterLevel = BeginWaterLevel;
            BeginWaterVolume = CalculateUtils.getVolumeFromLV(BeginWaterLevel, CurveLV, CurveLVLength);

            //求平均水位，求对应最大下泄能力MaxQelease和下泄流量
            EndWaterVolume = CalculateUtils.getVolumeFromLV(AssumedEndWaterLevel, CurveLV, CurveLVLength);
            Qrelease = (BeginWaterVolume - EndWaterVolume) * 100000000 / PeriodLength + OperationRunoffData[0][j] - EvaLoss[j];//单位：m^3/s
            MaxQelease = CalculateUtils.getMaxQreleaseFromLQ(AssumedEndWaterLevel / 2 + BeginWaterLevel / 2, CurveLQ, CurveLQLength);

            //入库流量小于限制下泄流量
            if (OperationRunoffData[0][j] <= XZQrelease) {
                // 来多少放多少，泄流=入流
                Qrelease = OperationRunoffData[0][j];

                EndWaterVolume = BeginWaterVolume - EvaLoss[j] / 100000000 * PeriodLength;
                CalculatedEndWaterLevel = CalculateUtils.getLevelFromLV(EndWaterVolume, CurveLV, CurveLVLength);
            } else if (OperationRunoffData[0][j] > XZQrelease) {
                // 来流大于限制下泄流量，小于非常泄洪，水库水位小于正常水位
                Qrelease = MaxQelease;

                EndWaterVolume = BeginWaterVolume + (OperationRunoffData[0][j] - EvaLoss[j] - Qrelease) / 100000000 * PeriodLength;
                CalculatedEndWaterLevel = CalculateUtils.getLevelFromLV(EndWaterVolume, CurveLV, CurveLVLength);
            }

            while (Math.abs(CalculatedEndWaterLevel - AssumedEndWaterLevel) > 0.5) {
                AssumedEndWaterLevel = AssumedEndWaterLevel + (CalculatedEndWaterLevel - AssumedEndWaterLevel) / 5;

                EndWaterVolume = CalculateUtils.getVolumeFromLV(AssumedEndWaterLevel, CurveLV, CurveLVLength);
                Qrelease = (BeginWaterVolume - EndWaterVolume) * 100000000 / PeriodLength + OperationRunoffData[0][j] - EvaLoss[j];
                MaxQelease = CalculateUtils.getMaxQreleaseFromLQ(AssumedEndWaterLevel / 2 + BeginWaterLevel / 2, CurveLQ, CurveLQLength);

                if (OperationRunoffData[0][j] <= XZQrelease) {
                    // 来多少放多少，泄流=入流
                    Qrelease = OperationRunoffData[0][j];

                    EndWaterVolume = BeginWaterVolume - EvaLoss[j] / 100000000 * PeriodLength;
                    CalculatedEndWaterLevel = CalculateUtils.getLevelFromLV(EndWaterVolume, CurveLV, CurveLVLength);
                } else if (OperationRunoffData[0][j] > XZQrelease) {
                    // 来流大于限制下泄流量，小于非常泄洪，水库水位小于正常水位
                    Qrelease = MaxQelease;

                    EndWaterVolume = BeginWaterVolume + (OperationRunoffData[0][j] - EvaLoss[j] - Qrelease) / 100000000 * PeriodLength;
                    CalculatedEndWaterLevel = CalculateUtils.getLevelFromLV(EndWaterVolume, CurveLV, CurveLVLength);
                }
            }

            BeginWaterLevel = CalculatedEndWaterLevel;
            QreleaseArray[0][j] = Qrelease;
            WaterLevelArray[0][j + 1] = CalculatedEndWaterLevel;
            WaterVolumeArray[0][j + 1] = CalculateUtils.getVolumeFromLV(WaterLevelArray[0][j + 1], CurveLV, CurveLVLength);
        }

        OutputArray = WaterLevelFallingProcess();

        for (int i = 0; i < NumPeriod; i++) {
            QreleaseArray[0][i] = OutputArray[0][i];
            WaterLevelArray[0][i] = OutputArray[1][i];
            WaterVolumeArray[0][i] = OutputArray[2][i];
        }
        WaterLevelArray[0][NumPeriod] = OutputArray[1][NumPeriod];
        WaterVolumeArray[0][NumPeriod] = OutputArray[2][NumPeriod];

        // 获取最大泄洪
        FloodDischarge = CalculateUtils.getMaxValueOfMatrix(QreleaseArray);

        // 获取最高水位
        MaxLevel = CalculateUtils.getMaxValueOfMatrix(WaterLevelArray);

        // 获取拦蓄水量
        FloodRetention = CalculateUtils.getVolumeFromLV(MaxLevel, CurveLV, CurveLVLength) - CalculateUtils.getVolumeFromLV(WaterLevelArray[0][0], CurveLV, CurveLVLength);

        // 获取削峰率
        PeakClippingRate = (FloodPeak - FloodDischarge) / FloodPeak * 100;

        int timeSpan1 = CalculateUtils.getMaxValueOfMatrixIndex(OperationRunoffData);
        int timeSpan2 = CalculateUtils.getMaxValueOfMatrixIndex(WaterLevelArray);
        int timeSpan3 = CalculateUtils.getMaxValueOfMatrixIndex(QreleaseArray);

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = simpleDateFormat.parse(startTime);
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(Calendar.HOUR, timeSpan1);
        Date date1 = instance.getTime();
        this.RKPeakTime = simpleDateFormat.format(date1);

        instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(Calendar.HOUR, timeSpan2);
        Date date2 = instance.getTime();
        this.MaxLevelTime = simpleDateFormat.format(date2);

        instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(Calendar.HOUR, timeSpan3);
        Date date3 = instance.getTime();
        this.CKPeakTime = simpleDateFormat.format(date3);

        /*
         * 计算 最高水位是否超标准、最高水位是否超汛限、末水位是否超汛限
         */
        this.MaxLevelExceedStandard = MaxLevel > SJWaterLevel;
        this.MaxLevelExceedLimit = MaxLevel > FloodLevel;
        this.EndLevelExceedLimit = WaterLevelArray[0][NumPeriod - 1] > FloodLevel;

        /*
         * 计算 出库洪量、期末水位、调度期末水位
         */
        this.CKVolume = FloodVolume - FloodRetention;
        this.EndLevel = WaterLevelArray[0][NumPeriod - 1];
        this.InitialLevel = WaterLevelArray[0][0];

        /*
         * 写入单水库结果和单闸坝结果
         */
        this.singleDamOutput = createSingleDamOutput();

        // 马斯京跟河道演算
        tempQ = oneChannel.msjg(getOptimalLine_Qrelease()[0]);
    }

    public double[][] WaterLevelFallingProcess() {

        double[][] OutputArray = new double[3][NumPeriod + 1];

        double MaxWaterLevel = 0;
        int MaxWaterLevelIndex = 0;
        double MaxQreleaseinProcess = 0;

        // 找到最高水位及其时段编号
        for (int j = 0; j < NumPeriod; j++) {
            if (MaxWaterLevel < WaterLevelArray[0][j]) {
                MaxWaterLevel = WaterLevelArray[0][j];
                MaxWaterLevelIndex = j;
            }
        }
        // 找出最大下泄流量值
        for (int j = 0; j < NumPeriod; j++) {
            if (MaxQreleaseinProcess < QreleaseArray[0][j]) {
                MaxQreleaseinProcess = QreleaseArray[0][j];
            }
        }

        // 对后半段，按照已经发生的最大流量下泄，考虑下泄能力约束
        double BeginWaterLevel = 0;
        double CalculatedEndWaterLevel = 0; //计算得到的末水位
        double AssumedEndWaterLevel = 0; //假设的末水位
        double BeginWaterVolume = 0;
        double EndWaterVolume = 0;
        BeginWaterLevel = WaterLevelArray[0][MaxWaterLevelIndex];

        for (int j = MaxWaterLevelIndex; j <= NumPeriod - 1; j++) {
            double MaxQelease = 0;
            double Qrelease = 0;
            // 先假设一个末水位，获取末库容
            AssumedEndWaterLevel = BeginWaterLevel;
            CalculatedEndWaterLevel = BeginWaterLevel;
            BeginWaterVolume = CalculateUtils.getVolumeFromLV(BeginWaterLevel, CurveLV, CurveLVLength);


            EndWaterVolume = CalculateUtils.getVolumeFromLV(AssumedEndWaterLevel, CurveLV, CurveLVLength);
            MaxQelease = CalculateUtils.getMaxQreleaseFromLQ(AssumedEndWaterLevel / 2 + BeginWaterLevel / 2, CurveLQ, CurveLQLength);
            Qrelease = MaxQreleaseinProcess;

            if (Qrelease > MaxQelease) {
                Qrelease = MaxQelease;
            }//如果计算出来的需要释放的流量 Qrelease 超过了最大可释放流量 MaxQelease，则将实际释放的流量 Qrelease 设置为 MaxQelease，以确保不超过系统能够承受的最大流量。
            EndWaterVolume = BeginWaterVolume + (OperationRunoffData[0][j] - EvaLoss[j] - Qrelease) / 100000000 * PeriodLength;
            CalculatedEndWaterLevel = CalculateUtils.getLevelFromLV(EndWaterVolume, CurveLV, CurveLVLength);
            if (CalculatedEndWaterLevel < FloodLevel) {
                CalculatedEndWaterLevel = FloodLevel;//如果计算得到的末水位 CalculatedEndWaterLevel 小于设定的防洪水位 FloodLevel,将末水位 CalculatedEndWaterLevel 调整为防洪水位 FloodLevel
                EndWaterVolume = CalculateUtils.getVolumeFromLV(CalculatedEndWaterLevel, CurveLV, CurveLVLength);
                Qrelease = (BeginWaterVolume - EndWaterVolume) * 100000000 / PeriodLength + OperationRunoffData[0][j] - EvaLoss[j];
            }

            while (Math.abs(CalculatedEndWaterLevel - AssumedEndWaterLevel) > 0.0001) {
                AssumedEndWaterLevel = AssumedEndWaterLevel + (CalculatedEndWaterLevel - AssumedEndWaterLevel) / 5;
                EndWaterVolume = CalculateUtils.getVolumeFromLV(AssumedEndWaterLevel, CurveLV, CurveLVLength);
                MaxQelease = CalculateUtils.getMaxQreleaseFromLQ(AssumedEndWaterLevel / 2 + BeginWaterLevel / 2, CurveLQ, CurveLQLength);
                Qrelease = MaxQreleaseinProcess;
                if (Qrelease > MaxQelease) {
                    Qrelease = MaxQelease;
                }
                EndWaterVolume = BeginWaterVolume + (OperationRunoffData[0][j] - EvaLoss[j] - Qrelease) / 100000000 * PeriodLength;
                CalculatedEndWaterLevel = CalculateUtils.getLevelFromLV(EndWaterVolume, CurveLV, CurveLVLength);
                if (CalculatedEndWaterLevel < FloodLevel) {
                    CalculatedEndWaterLevel = FloodLevel;
                    EndWaterVolume = CalculateUtils.getVolumeFromLV(CalculatedEndWaterLevel, CurveLV, CurveLVLength);
                    Qrelease = (BeginWaterVolume - EndWaterVolume) * 100000000 / PeriodLength + OperationRunoffData[0][j] - EvaLoss[j];
                }
            }

            BeginWaterLevel = CalculatedEndWaterLevel;
            QreleaseArray[0][j] = Qrelease;
            WaterLevelArray[0][j + 1] = CalculatedEndWaterLevel;
            WaterVolumeArray[0][j + 1] = CalculateUtils.getVolumeFromLV(WaterLevelArray[0][j + 1], CurveLV, CurveLVLength);

        }

        for (int i = 0; i < NumPeriod; i++) {
            OutputArray[0][i] = QreleaseArray[0][i];
            OutputArray[1][i] = WaterLevelArray[0][i];
            OutputArray[2][i] = WaterVolumeArray[0][i];
        }
        OutputArray[1][NumPeriod] = WaterLevelArray[0][NumPeriod];
        OutputArray[2][NumPeriod] = WaterVolumeArray[0][NumPeriod];

        return OutputArray;
    }

    /**
     * 将计算出的结果写入到 SingleDamOutput 属性
     *
     * @return
     * @throws Exception
     */
    public SingleDamOutput createSingleDamOutput() throws Exception {
        SingleDamOutput singleDamOutput = new SingleDamOutput();

        List<ReserviorTimeResult> timeDataList = new ArrayList<>(); // 时间,时段编号

        List<Double> TempOperationRunoffData = new ArrayList<>();// 入库来水

        List<Double> TempUpLevelLimitArray = new ArrayList<>();// 各个时段水位上限

        List<Double> TempDownLevelLimitArray = new ArrayList<>();// 各个时段水位下限

        List<Double> TempUpQreleaseLimitArray = new ArrayList<>();// 各时段下泄流量上限

        List<Double> TempDownQreleaseLimitArray = new ArrayList<>();// 各时段下泄流量下限

        List<Double> TempWaterVolumeArray = new ArrayList<>();// 最优过程的时段初库容

        List<Double> TempWaterLevelArray = new ArrayList<>();// 最优过程的时段初水位

        List<Double> TempMaxQreleaseFromLQ = new ArrayList<Double>();// 各时段最大泄流能力

        List<Double> TempQreleaseArray = new ArrayList<>();// 各时段下泄流量

        List<Double> TempIntervalRunoffData = new ArrayList<Double>();// 各时段区间入流

        List<Double> TempDownstreamSectionInflow = new ArrayList<Double>();// 下游断面各时段来流

        ReserviorTimeResult Temp = new ReserviorTimeResult();

        for (int j = 0; j < NumPeriod; j++) {
            Temp.setTime(String.valueOf(j));
            timeDataList.add(Temp);
            Temp = new ReserviorTimeResult();
        }
        singleDamOutput.setTimeDataList(timeDataList);

        for (int j = 0; j < NumPeriod; j++) {
            TempOperationRunoffData.add(OperationRunoffData[0][j]);
        }
        singleDamOutput.setOperationRunoffData(TempOperationRunoffData);

        for (int j = 0; j < NumPeriod; j++) {
            TempUpLevelLimitArray.add(UpLevelLimitArray[j]);
        }
        singleDamOutput.setUpLevelLimitArray(TempUpLevelLimitArray);

        for (int j = 0; j < NumPeriod; j++) {
            TempDownLevelLimitArray.add(DownLevelLimitArray[j]);
        }
        singleDamOutput.setDownLevelLimitArray(TempDownLevelLimitArray);

        for (int j = 0; j < NumPeriod; j++) {
            TempMaxQreleaseFromLQ.add(CalculateUtils.getMaxQreleaseFromLQ(CalculateUtils.getLevelFromLV(WaterVolumeArray[0][j] / 100000000, CurveLV, CurveLVLength), CurveLQ, CurveLQLength));
        }
        singleDamOutput.setMaxQreleaseFromLQ(TempMaxQreleaseFromLQ);

        for (int j = 0; j < NumPeriod; j++) {
            TempUpQreleaseLimitArray.add(UpQreleaseLimitArray[j]);
        }
        singleDamOutput.setUpQreleaseLimitArray(TempUpQreleaseLimitArray);

        for (int j = 0; j < NumPeriod; j++) {
            TempDownQreleaseLimitArray.add(DownQreleaseLimitArray[j]);
        }
        singleDamOutput.setDownQreleaseLimitArray(TempDownQreleaseLimitArray);

        for (int j = 0; j < NumPeriod; j++) {
            TempWaterLevelArray.add(WaterLevelArray[0][j]);
        }
        singleDamOutput.setOptimalLine_Level(TempWaterLevelArray);

        for (int j = 0; j < NumPeriod; j++) {
            TempWaterVolumeArray.add(WaterVolumeArray[0][j] / 100000000);
        }
        singleDamOutput.setOptimalLine_Volume(TempWaterVolumeArray);

        for (int j = 0; j < NumPeriod; j++) {
            TempQreleaseArray.add(QreleaseArray[0][j]);
        }
        singleDamOutput.setOptimalLine_Qrelease(TempQreleaseArray);

        singleDamOutput.setFloodPeak(FloodPeak);

        singleDamOutput.setFloodVolume(FloodVolume);

        singleDamOutput.setFloodDischarge(FloodDischarge);

        singleDamOutput.setFloodRetention(FloodRetention);

        singleDamOutput.setPeakClippingRate(PeakClippingRate);

        String Judge1 = MaxLevelExceedStandard ? "最高水位超标准" : "最高水位不超标准";
        String Judge2 = MaxLevelExceedLimit ? "最高水位超汛限" : "最高水位不超汛限";
        String Judge3 = EndLevelExceedLimit ? "期末水位超汛限" : "期末水位不超汛限";

        singleDamOutput.setRKPeakTime(RKPeakTime);

        singleDamOutput.setMaxLevelTime(MaxLevelTime);

        singleDamOutput.setCKPeakTime(CKPeakTime);

        String suggestionString = String.format("%s %s %.1f %s %d %s %.2f %s %s %s %.2f %s %s %s %.1f %s %s %s %s %s %s " + "%.2f %s %.2f %s %.1f %s %s", ReservoirName, "调度建议：初始水位为", WaterLevelArray[0][0], "米，未来", NumPeriod, "h内水库最大入库流量为", FloodPeak, "立方米/秒，入库峰现时间为", RKPeakTime, "入库洪水总量为", FloodVolume, "亿立方米，预计水库将会在", MaxLevelTime, "达到调洪最高水位", MaxLevel, "米", Judge1, Judge2, "水库控制过程中为在保证水库安全的前提下保证下游河道安全，下泄流量在", CKPeakTime, "达到峰值", FloodDischarge, "立方米/秒，出库洪水总量为", CKVolume, "亿立方米，期末水位为", EndLevel, "米", Judge3);

        singleDamOutput.setSuggestion(suggestionString);

        for (int j = 0; j < NumPeriod; j++) {
            TempIntervalRunoffData.add(IntervalRunoffData[0][j]);
        }
        singleDamOutput.setIntervalRunoffData(TempIntervalRunoffData);

        for (int j = 0; j < NumPeriod; j++) {
            TempDownstreamSectionInflow.add(QreleaseArray[0][j] + IntervalRunoffData[0][j]);
        }
        singleDamOutput.setDownstreamSectionInflow(TempDownstreamSectionInflow);

        /*
         * 设置 最高水位是否超标准、最高水位是否超汛限、末水位是否超汛限（3 个 boolean 值）
         */
        singleDamOutput.setMaxLevelExceedStandard(MaxLevelExceedStandard);
        singleDamOutput.setMaxLevelExceedLimit(MaxLevelExceedLimit);
        singleDamOutput.setEndLevelExceedLimit(EndLevelExceedLimit);

        /*
         * 设置 入库洪量、调度期末水位、初始水位
         */
        singleDamOutput.setCKVolume(CKVolume);
        singleDamOutput.setEndLevel(EndLevel);
        singleDamOutput.setInitialLevel(InitialLevel);
        singleDamOutput.setMaxLevel(MaxLevel);

        /*
         * 计算超标准水位时段和超讯限水位时段
         */
        String overStandard = CalculateUtils.overWaterLevel(singleDamOutput.getOptimalLine_Level(), SJWaterLevel, startTime);
        singleDamOutput.setOverStandardTime(overStandard);

        String overLimit = CalculateUtils.overWaterLevel(singleDamOutput.getOptimalLine_Level(), FloodLevel, startTime);
        singleDamOutput.setOverLimitTime(overLimit);

        return singleDamOutput;
    }

    /**
     * 输出详细结果到 txt 文件
     *
     * @param path
     * @throws IOException
     * @throws ParseException
     */
    public void OutputResults(String path) throws IOException, ParseException {
        String OneLine;
        ArrayList<String> list = new ArrayList<String>();
        DecimalFormat myDF;
        OneLine = String.format("%-11s", "时段编号") + String.format("%-11s", "上游来水") + String.format("%-11s", "时初水位") + String.format("%-11s", "时初库容") + String.format("%-11s", "泄流能力") + String.format("%-11s", "下泄流量");
        list.add(OneLine);
        for (int i = 0; i < NumOperationYear; i++) {
            for (int j = 0; j < NumPeriod; j++) {
                myDF = new DecimalFormat("0");
                String s1 = "";//String.format("%-15s", "时段编号")
                String s2 = "";//String.format("%-15s", "上游来水")

                String s5 = "";//String.format("%-15s", "时初水位")
                String s6 = "";//String.format("%-15s", "时初库容")

                String s12_1 = "";//String.format("%-15s", "泄流能力")
                String s13 = "";//String.format("%-15s", "下泄流量")

                myDF = new DecimalFormat("0");
                s1 = String.format("%-15s", myDF.format(i) + " " + myDF.format(j));
                myDF = new DecimalFormat("0.00");
                s2 = String.format("%-15s", myDF.format(OperationRunoffData[i][j])) + "  ";

                myDF = new DecimalFormat("0.0000");
                s5 = String.format("%-15s", myDF.format(WaterLevelArray[0][j])) + "  ";
                myDF = new DecimalFormat("0.0000");
                s6 = String.format("%-15s", myDF.format(WaterVolumeArray[0][j])) + "  ";

                myDF = new DecimalFormat("0.0");
                s12_1 = String.format("%-15s", myDF.format(CalculateUtils.getMaxQreleaseFromLQ(WaterLevelArray[0][j] / 2 + WaterLevelArray[0][j + 1] / 2, CurveLQ, CurveLQLength))) + "  ";
                myDF = new DecimalFormat("0.0");
                s13 = String.format("%-15s", myDF.format(QreleaseArray[0][j])) + "  ";

                OneLine = s1 + s2 + s5 + s6 + s12_1 + s13;
                list.add(OneLine);
                OneLine = "";
            }
        }

        String Judge1 = MaxLevel > SJWaterLevel ? "最高水位超标准" : "最高水位不超标准";
        String Judge2 = MaxLevel > FloodLevel ? "最高水位超汛限" : "最高水位不超汛限";
        String Judge3 = WaterLevelArray[0][NumPeriod] > FloodLevel ? "期末水位超汛限" : "期末水位不超汛限";

        int timeSpan1 = CalculateUtils.getMaxValueOfMatrixIndex(OperationRunoffData);
        int timeSpan2 = CalculateUtils.getMaxValueOfMatrixIndex(WaterLevelArray);
        int timeSpan3 = CalculateUtils.getMaxValueOfMatrixIndex(QreleaseArray);

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = simpleDateFormat.parse(startTime);
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(Calendar.HOUR, timeSpan1);
        Date date1 = instance.getTime();

        instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(Calendar.HOUR, timeSpan2);
        Date date2 = instance.getTime();

        instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(Calendar.HOUR, timeSpan3);
        Date date3 = instance.getTime();

        String FloodPeakString = String.format("%s %.2f %s", "入库洪峰：", FloodPeak, " 立方米/秒");
        String FloodVolumeString = String.format("%s %.2f %s", "入库洪量：", FloodVolume, " 亿立方米");
        String FloodDischargeString = String.format("%s %.2f %s", "最大泄洪：", FloodDischarge, " 立方米/秒");
        String FloodRetentionString = String.format("%s %.2f %s", "拦蓄水量：", FloodRetention, " 亿立方米");
        String PeakClippingRateString = String.format("%s %.2f %s", "削峰率：", PeakClippingRate, "%");
        String SuggestionConvention = String.format("%s %s %.1f %s %d %s %.2f %s %s %s %.2f %s %s %s %.1f %s %s %s %s %s %s %.2f %s %.2f %s %.1f %s %s", ReservoirName, "调度建议：初始水位为", WaterLevelArray[0][0], "米，未来", NumPeriod, "h内水库最大入库流量为", FloodPeak, "立方米/秒，入库峰现时间为", simpleDateFormat.format(date1), "入库洪水总量为", FloodVolume, "亿立方米，预计水库将会在", simpleDateFormat.format(date2), "达到调洪最高水位", MaxLevel, "米", Judge1, Judge2, "水库控制过程中为在保证水库安全的前提下保证下游河道安全，下泄流量在", simpleDateFormat.format(date3), "达到峰值", FloodDischarge, "立方米/秒，出库洪水总量为", CKVolume, "亿立方米，期末水位为", WaterLevelArray[0][NumPeriod], "米", Judge3);
        list.add(FloodPeakString);
        list.add(FloodVolumeString);
        list.add(FloodDischargeString);
        list.add(FloodRetentionString);
        list.add(PeakClippingRateString);
        list.add(SuggestionConvention);

        File file = new File(path);
        FileUtils.writeLines(file, "UTF-8", list);

        // 输出上游水库区间流量结果到TXT文件
//        path = folderPath + File.separator + strResCode + File.separator + "IntervalRunoffDataofUpstreamReservoir.txt";
//        list = new ArrayList<String>();
//        myDF = new DecimalFormat("0");
//        OneLine = "";
//        for (int i = 0; i < NumOperationYear; i++) {
//            for (int j = 0; j < NumPeriod; j++) {
//                String s = "";// String.format("%-15s", "下游控制断面流量")
//
//                myDF = new DecimalFormat("0.00");
//                s = myDF.format(QreleaseArray[0][j] + IntervalRunoffData[i][j]);
//                OneLine = OneLine + s + "\t";
//
//            }
//            list.add(OneLine);
//            OneLine = "";
//        }
//        file = new File(path);
//        FileUtils.writeLines(file, "UTF-8", list);
    }
}
